﻿// <copyright file="DifferentialSftHeader.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// This class represents the Differential SFT Header section in an App-V SFT/DSFT file.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Although normal SFT files always begin with the SMFF header metadata section,
    /// differential SFTs, introduced in version 4.5, add an extra header metadata section
    /// before the SMFF section and all the other sections.
    /// </para>
    /// <para>
    /// This header section’s purpose is to carry indices to the so-called base-SFT (the SFT
    /// file that the differential SFT is based on). These index entries tell the offsets and
    /// sizes of both the FB1 and FB2 parts and the points where the newest data blocks on the
    /// base SFT start.
    /// </para>
    /// <para>
    /// All the other header metadata sections in the differential SFT are exact copies of the
    /// ones found in the base SFT. The data blocks are in reduced form so that the DSFT file
    /// contains only the data blocks that have been changed in that particular version of
    /// the package.
    /// </para>
    /// <para>
    /// This section should not be compressed.
    /// </para>
    /// </remarks>
    public class DifferentialSftHeader : HeaderMetadataSection
    {
        /// <summary>
        /// The total length of all data blocks in FB1 belonging to the old versions of this
        /// package, i.e., those blocks that make up any and all files not updated in the
        /// current version of SFT.
        /// </summary>
        private long sizeOldBlocksFB1;

        /// <summary>
        /// The total length of all data blocks in FB2 belonging to the old versions of this
        /// package, i.e., those blocks that make up any and all files not updated in the
        /// current version of SFT.
        /// </summary>
        private long sizeOldBlocksFB2;

        /// <summary>
        /// The total length of all data blocks in FB1 belonging to the files updated in
        /// this version of the package. These data blocks are also the ones that get
        /// physically included in the DSFT file.
        /// </summary>
        private long sizeNewBlocksFB1;

        /// <summary>
        /// The total length of all data blocks in FB2 belonging to the files updated in
        /// this version of the package. These data blocks are also the ones that get
        /// physically included in the DSFT file.
        /// </summary>
        private long sizeNewBlocksFB2;

        /// <summary>
        /// The absolute byte offset inside the base SFT file to the beginning of part
        /// of FB1 making up the blocks updated in this version of the file.
        /// </summary>
        private long offsetNewBlocksFB1;

        /// <summary>
        /// The absolute byte offset inside the base SFT file to the beginning of part
        /// of FB2 making up the blocks updated in this version of the file.
        /// </summary>
        private long offsetNewBlocksFB2;

        /// <summary>
        /// The total length of all the blocks in FB1.
        /// </summary>
        private long sizeFB1;

        /// <summary>
        /// The total length of all the blocks in FB2.
        /// </summary>
        private long sizeFB2;

        /// <summary>
        /// The absolute byte offset inside the base SFT file to the beginning of part
        /// of FB1 making up the blocks not updated in this version of the file.
        /// </summary>
        private long offsetOldBlocksFB1;

        /// <summary>
        /// The absolute byte offset inside the base SFT file to the beginning of part
        /// of FB2 making up the blocks not updated in this version of the file.
        /// </summary>
        private long offsetOldBlocksFB2;

        /// <summary>
        /// This field is reserved and is initialized to all zeroes.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "This field is marked as reserved in the App-V File Format Specification.")]
        private byte[] reserved;
       
        /// <summary>
        /// Initializes a new instance of the DifferentialSftHeader class.
        /// </summary>
        /// <param name="fileReader">A binary file reader that points to the SFT file.</param>
        public DifferentialSftHeader(System.IO.BinaryReader fileReader)
            : base(fileReader)
        {
        }

        /// <summary>
        /// Gets the total length of all data blocks in FB1 belonging to the old versions of this
        /// package, i.e., those blocks that make up any and all files not updated in the
        /// current version of SFT.
        /// </summary>
        public long SizeOldBlocksFB1
        {
            get
            {
                this.ReadSection();
                return this.sizeOldBlocksFB1;
            }
        }

        /// <summary>
        /// Gets the total length of all data blocks in FB2 belonging to the old versions of this
        /// package, i.e., those blocks that make up any and all files not updated in the
        /// current version of SFT.
        /// </summary>
        public long SizeOldBlocksFB2
        {
            get
            {
                this.ReadSection();
                return this.sizeOldBlocksFB2;
            }
        }

        /// <summary>
        /// Gets the total length of all data blocks in FB1 belonging to the files updated in
        /// this version of the package. These data blocks are also the ones that get
        /// physically included in the DSFT file.
        /// </summary>
        public long SizeNewBlocksFB1
        {
            get
            {
                this.ReadSection();
                return this.sizeNewBlocksFB1;
            }
        }

        /// <summary>
        /// Gets the total length of all data blocks in FB2 belonging to the files updated in
        /// this version of the package. These data blocks are also the ones that get
        /// physically included in the DSFT file.
        /// </summary>
        public long SizeNewBlocksFB2
        {
            get
            {
                this.ReadSection();
                return this.sizeNewBlocksFB2;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the base SFT file to the beginning of part
        /// of FB1 making up the blocks updated in this version of the file.
        /// </summary>
        public long OffsetNewBlocksFB1
        {
            get
            {
                this.ReadSection();
                return this.offsetNewBlocksFB1;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the base SFT file to the beginning of part
        /// of FB2 making up the blocks updated in this version of the file.
        /// </summary>
        public long OffsetNewBlocksFB2
        {
            get
            {
                this.ReadSection();
                return this.offsetNewBlocksFB2;
            }
        }

        /// <summary>
        /// Gets the total length of all the blocks in FB1.
        /// </summary>
        public long SizeFB1
        {
            get
            {
                this.ReadSection();
                return this.sizeFB1;
            }
        }

        /// <summary>
        /// Gets the total length of all the blocks in FB2.
        /// </summary>
        public long SizeFB2
        {
            get
            {
                this.ReadSection();
                return this.sizeFB2;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the base SFT file to the beginning of part
        /// of FB1 making up the blocks not updated in this version of the file.
        /// </summary>
        public long OffsetOldBlocksFB1
        {
            get
            {
                this.ReadSection();
                return this.offsetOldBlocksFB1;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the base SFT file to the beginning of part
        /// of FB2 making up the blocks not updated in this version of the file.
        /// </summary>
        public long OffsetOldBlocksFB2
        {
            get
            {
                this.ReadSection();
                return this.offsetOldBlocksFB2;
            }
        }
        
        /// <summary>
        /// Reads the actual data for this section from the file.
        /// </summary>
        protected override void ReadSection()
        {
            if (this.SectionHasBeenRead)
            {
                return;
            }

            // Seek to the start of the data area for this section.
            long currentPosition = FileReader.BaseStream.Position;
            FileReader.BaseStream.Seek(this.StartPositionInFile + CommonSectionHeader.HeaderSize, System.IO.SeekOrigin.Begin);

            this.sizeOldBlocksFB1 = FileReader.ReadInt64();
            this.sizeOldBlocksFB2 = FileReader.ReadInt64();
            this.sizeNewBlocksFB1 = FileReader.ReadInt64();
            this.sizeNewBlocksFB2 = FileReader.ReadInt64();
            this.offsetNewBlocksFB1 = FileReader.ReadInt64();
            this.offsetNewBlocksFB2 = FileReader.ReadInt64();
            this.sizeFB1 = FileReader.ReadInt64();
            this.sizeFB2 = FileReader.ReadInt64();
            this.offsetOldBlocksFB1 = FileReader.ReadInt64();
            this.offsetOldBlocksFB2 = FileReader.ReadInt64();
            this.reserved = FileReader.ReadBytes(32);
       
            this.SectionHasBeenRead = true;
            FileReader.BaseStream.Seek(currentPosition, System.IO.SeekOrigin.Begin);
        }
    }
}
